home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / ghostview / Ghostview.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  59KB  |  1,938 lines

  1. /*
  2.  * Ghostview.c -- Ghostview widget.
  3.  * Copyright (C) 1992  Timothy O. Theisen
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  *   Author: Tim Theisen           Systems Programmer
  20.  * Internet: tim@cs.wisc.edu       Department of Computer Sciences
  21.  *     UUCP: uwvax!tim             University of Wisconsin-Madison
  22.  *    Phone: (608)262-0438         1210 West Dayton Street
  23.  *      FAX: (608)262-9777         Madison, WI   53706
  24.  */
  25.  
  26. #include <X11/IntrinsicP.h>
  27. #include <X11/StringDefs.h>
  28. #include <X11/Xatom.h>
  29. #include <X11/Xproto.h>
  30. #include <X11/Xos.h>
  31. #include "GhostviewP.h"
  32. #include <ctype.h>
  33.  
  34. #ifndef XlibSpecificationRelease
  35. typedef char *XPointer;
  36. #endif
  37.  
  38. #include <signal.h>
  39. #ifdef SIGNALRETURNSINT
  40. #define SIGVAL int
  41. #else
  42. #define SIGVAL void
  43. #endif
  44.  
  45. #ifdef NON_BLOCKING_IO
  46. #include <fcntl.h>
  47. /* if POSIX O_NONBLOCK is not available, use O_NDELAY */
  48. #if !defined(O_NONBLOCK) && defined(O_NDELAY)
  49. #define O_NONBLOCK O_NDELAY
  50. #endif
  51. #endif
  52.  
  53. #include <errno.h>
  54. /* BSD 4.3 errno.h does not declare errno */
  55. extern int errno;
  56. /* Both error returns are checked for non-blocking I/O. */
  57. /* Manufacture the other error code if only one exists. */
  58. #if !defined(EWOULDBLOCK) && defined(EAGAIN)
  59. #define EWOULDBLOCK EAGAIN
  60. #endif
  61. #if !defined(EAGAIN) && defined(EWOULDBLOCK)
  62. #define EAGAIN EWOULDBLOCK
  63. #endif
  64.  
  65. #ifndef VMS
  66. /* GV_BUFSIZ is set to the minimum POSIX PIPE_BUF to ensure that
  67.  * nonblocking writes to ghostscript will work properly.
  68.  */
  69. #define GV_BUFSIZ 512
  70. #else /* VMS */
  71. /*
  72. ** GV_BUFSIZ is the maximum length line we can handle, so we up it to 1024
  73. */
  74. #define GV_BUFSIZ 1024
  75. #endif /* VMS */
  76.  
  77. /* length calculates string length at compile time */
  78. /* can only be used with character constants */
  79. #define length(a) (sizeof(a)-1)
  80. #define iscomment(a, b) (strncmp(a, b, length(b)) == 0)
  81.  
  82. static void ComputeXdpi();
  83. static void ComputeYdpi();
  84.  
  85. static XtResource resources[] = {
  86. #define offset(field) XtOffsetOf(GhostviewRec, ghostview.field)
  87.     { XtNbottomMargin, XtCMargin, XtRInt, sizeof(int),
  88.       offset(bottom_margin), XtRImmediate, (XtPointer)0 },
  89.     { XtNbusyCursor, XtCCursor, XtRCursor, sizeof(XtPointer),
  90.       offset(busy_cursor), XtRString, "watch" },
  91.     { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  92.       offset(callback), XtRCallback, (XtPointer)NULL },
  93.     { XtNcursor, XtCCursor, XtRCursor, sizeof(XtPointer),
  94.       offset(cursor), XtRString, "crosshair" },
  95.     { XtNfilename, XtCFilename, XtRString, sizeof(String),
  96.       offset(filename), XtRString, (XtPointer)NULL },
  97.     { XtNinterpreter, XtCInterpreter, XtRString, sizeof(String),
  98.       offset(interpreter), XtRString, "gs" },
  99.     { XtNleftMargin, XtCMargin, XtRInt, sizeof(int),
  100.       offset(left_margin), XtRImmediate, (XtPointer)0 },
  101.     { XtNllx, XtCBoundingBox, XtRInt, sizeof(int),
  102.       offset(llx), XtRImmediate, (XtPointer)0 },
  103.     { XtNlly, XtCBoundingBox, XtRInt, sizeof(int),
  104.       offset(lly), XtRImmediate, (XtPointer)0 },
  105.     { XtNmessageCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  106.       offset(message_callback), XtRCallback, (XtPointer)NULL },
  107.     { XtNorientation, XtCOrientation, XtRPageOrientation,
  108.       sizeof(XtPageOrientation), offset(orientation), XtRImmediate,
  109.       (XtPointer)XtPageOrientationPortrait },
  110.     { XtNoutputCallback, XtCCallback, XtRCallback, sizeof(XtPointer),
  111.       offset(output_callback), XtRCallback, (XtPointer)NULL },
  112.     { XtNpreload, XtCPreload, XtRString, sizeof(String),
  113.       offset(preload), XtRString, (XtPointer)NULL },
  114.     { XtNquiet, XtCQuiet, XtRBoolean, sizeof(Boolean),
  115.       offset(quiet), XtRImmediate, (XtPointer)True },
  116.     { XtNrightMargin, XtCMargin, XtRInt, sizeof(int),
  117.       offset(right_margin), XtRImmediate, (XtPointer)0 },
  118.     { XtNtopMargin, XtCMargin, XtRInt, sizeof(int),
  119.       offset(top_margin), XtRImmediate, (XtPointer)0 },
  120.     { XtNuseBackingPixmap, XtCUseBackingPixmap, XtRBoolean, sizeof(Boolean),
  121.       offset(use_bpixmap), XtRImmediate, (XtPointer)True },
  122.     { XtNurx, XtCBoundingBox, XtRInt, sizeof(int),
  123.       offset(urx), XtRImmediate, (XtPointer)612 },
  124.     { XtNury, XtCBoundingBox, XtRInt, sizeof(int),
  125.       offset(ury), XtRImmediate, (XtPointer)792 },
  126.     { XtNxdpi, XtCResolution, XtRFloat, sizeof(float),
  127.       offset(xdpi), XtRCallProc, (XtPointer)ComputeXdpi },
  128.     { XtNydpi, XtCResolution, XtRFloat, sizeof(float),
  129.       offset(ydpi), XtRCallProc, (XtPointer)ComputeYdpi },
  130. #undef offset
  131. };
  132.  
  133. static void Message();
  134. static void Notify();
  135. static void Input();
  136. static void Output();
  137.  
  138. static void ClassInitialize();
  139. static void ClassPartInitialize();
  140. static void Initialize();
  141. static void Realize();
  142. static void Destroy();
  143. static void Resize();
  144. static Boolean SetValues();
  145. static XtGeometryResult QueryGeometry();
  146.  
  147. static void Layout();
  148. static Boolean ComputeSize();
  149. static void ChangeSize();
  150. static Boolean Setup();
  151. static void StartInterpreter();
  152. static void StopInterpreter();
  153. static void InterpreterFailed();
  154.  
  155. static XtActionsRec actions[] =
  156. {
  157.     {"message",    Message},
  158.     {"notify",    Notify},
  159. };
  160.  
  161. /* notify takes zero to four parameters.  The first two give the width and
  162.  * height of the zoom requested in the default user coordinate system.
  163.  * If they are omitted, a default value of 72 is provided.  If the second
  164.  * parameter is omitted, the zoom area is assumed to be a square.
  165.  * The next two parameters give the desired resolution of the zoom window.
  166.  * If they are omitted, a default value of 300 is provided. If the four
  167.  * parameter is omitted, the y resolution is assumed to be equal to the
  168.  * x resolution.
  169.  */
  170. static char translations[] =
  171. "<Message>:        message()    \n\
  172. <Btn1Down>:        notify(72)    \n\
  173. <Btn2Down>:        notify(108)    \n\
  174. <Btn3Down>:        notify(144)    \n\
  175. ";
  176.  
  177. GhostviewClassRec ghostviewClassRec = {
  178.   { /* core fields */
  179.     /* superclass        */    (WidgetClass) &coreClassRec,
  180.     /* class_name        */    "Ghostview",
  181.     /* widget_size        */    sizeof(GhostviewRec),
  182.     /* class_initialize        */    ClassInitialize,
  183.     /* class_part_initialize    */    ClassPartInitialize,
  184.     /* class_inited        */    FALSE,
  185.     /* initialize        */    Initialize,
  186.     /* initialize_hook        */    NULL,
  187.     /* realize            */    Realize,
  188.     /* actions            */    actions,
  189.     /* num_actions        */    XtNumber(actions),
  190.     /* resources        */    resources,
  191.     /* num_resources        */    XtNumber(resources),
  192.     /* xrm_class        */    NULLQUARK,
  193.     /* compress_motion        */    TRUE,
  194.     /* compress_exposure    */    TRUE,
  195.     /* compress_enterleave    */    TRUE,
  196.     /* visible_interest        */    FALSE,
  197.     /* destroy            */    Destroy,
  198.     /* resize            */    Resize,
  199.     /* expose            */    NULL,
  200.     /* set_values        */    SetValues,
  201.     /* set_values_hook        */    NULL,
  202.     /* set_values_almost    */    XtInheritSetValuesAlmost,
  203.     /* get_values_hook        */    NULL,
  204.     /* accept_focus        */    NULL,
  205.     /* version            */    XtVersion,
  206.     /* callback_private        */    NULL,
  207.     /* tm_table            */    translations,
  208.     /* query_geometry        */    QueryGeometry,
  209.     /* display_accelerator    */    XtInheritDisplayAccelerator,
  210.     /* extension        */    NULL
  211.   },
  212.   { /* ghostview fields */
  213.     /* ghostview        */    NULL,
  214.     /* next            */    NULL,
  215.     /* page            */    NULL,
  216.     /* done            */    NULL
  217.   }
  218. };
  219.  
  220. WidgetClass ghostviewWidgetClass = (WidgetClass)&ghostviewClassRec;
  221.  
  222. /* Procedures that compute the default xdpi and ydpi from display parameters */
  223.  
  224. static void
  225. ComputeXdpi(w, offset, value)
  226.     Widget w;
  227.     int offset;
  228.     XrmValue *value;
  229. {
  230.     static float xdpi;
  231.     xdpi = 25.4 * WidthOfScreen(XtScreen(w)) / WidthMMOfScreen(XtScreen(w));
  232.     value->addr = (XtPointer) &xdpi;
  233. }
  234.  
  235. static void
  236. ComputeYdpi(w, offset, value)
  237.     Widget w;
  238.     int offset;
  239.     XrmValue *value;
  240. {
  241.     static float ydpi;
  242.     ydpi = 25.4 * HeightOfScreen(XtScreen(w)) / HeightMMOfScreen(XtScreen(w));
  243.     value->addr = (XtPointer) &ydpi;
  244. }
  245.  
  246. /* Message action routine.
  247.  * Passes ghostscript message events back to application via
  248.  * the message callback.  It also marks the interpreter as
  249.  * being not busy at the end of page, and stops the interpreter
  250.  * when it send a "done" message.
  251.  */
  252. static void
  253. Message(w, event, params, num_params)
  254.     Widget w;
  255.     XEvent *event;
  256.     String *params;        /* unused */
  257.     Cardinal *num_params;    /* unused */
  258. {
  259.     GhostviewWidget gvw = (GhostviewWidget) w;
  260.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  261.  
  262.     gvw->ghostview.mwin = event->xclient.data.l[0];
  263.     if (event->xclient.message_type ==
  264.     XmuInternAtom(XtDisplay(w), gvc->ghostview_class.page)) {
  265.     gvw->ghostview.busy = False;
  266.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  267.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Page");
  268.     } else if (event->xclient.message_type ==
  269.            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.done)) {
  270.     StopInterpreter(w);
  271.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Done");
  272.     }
  273. }
  274.  
  275. /* Notify action routine.
  276.  * Calculates where the user clicked in the default user coordinate system.
  277.  * Call the callbacks with the point of click and size of zoom window
  278.  * requested.
  279.  */
  280. static void
  281. Notify(w, event, params, num_params)
  282.     Widget w;
  283.     XEvent *event;
  284.     String *params;
  285.     Cardinal *num_params;
  286. {
  287.     GhostviewWidget gvw = (GhostviewWidget) w;
  288.     GhostviewReturnStruct ret_val;
  289.  
  290.     /* notify takes zero to four parameters.  The first two give the width and
  291.      * height of the zoom requested in the default user coordinate system.
  292.      * If they are omitted, a default value of 72 is provided.  If the second
  293.      * parameter is omitted, the zoom area is assumed to be a square.
  294.      * The next two parameters give the desired resolution of the zoom window.
  295.      * If they are omitted, a default value of 300 is provided. If the four
  296.      * parameter is omitted, the y resolution is assumed to be equal to the
  297.      * x resolution.
  298.      */
  299.     switch (*num_params) {
  300.     case 0:
  301.     ret_val.width = ret_val.height = 72;
  302.     ret_val.xdpi = ret_val.ydpi = 300;
  303.     break;
  304.     case 1:
  305.     ret_val.width = ret_val.height = atoi(params[0]);
  306.     ret_val.xdpi = ret_val.ydpi = 300;
  307.     break;
  308.     case 2:
  309.     ret_val.width = atoi(params[0]);
  310.     ret_val.height = atoi(params[1]);
  311.     ret_val.xdpi = ret_val.ydpi = 300;
  312.     break;
  313.     case 3:
  314.     ret_val.width = atoi(params[0]);
  315.     ret_val.height = atoi(params[1]);
  316.     ret_val.xdpi = ret_val.ydpi = atoi(params[2]);
  317.     break;
  318.     default:
  319.     ret_val.width = atoi(params[0]);
  320.     ret_val.height = atoi(params[1]);
  321.     ret_val.xdpi = atoi(params[2]);
  322.     ret_val.ydpi = atoi(params[3]);
  323.     break;
  324.     }
  325.  
  326.     switch (gvw->ghostview.orientation) {
  327.     case XtPageOrientationPortrait:
  328.     ret_val.psx = gvw->ghostview.llx + 
  329.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  330.     ret_val.psy = gvw->ghostview.ury - 
  331.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  332.     break;
  333.     case XtPageOrientationLandscape:
  334.     ret_val.psx = gvw->ghostview.llx + 
  335.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  336.     ret_val.psy = gvw->ghostview.lly + 
  337.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  338.     break;
  339.     case XtPageOrientationUpsideDown:
  340.     ret_val.psx = gvw->ghostview.urx - 
  341.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  342.     ret_val.psy = gvw->ghostview.lly + 
  343.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  344.     break;
  345.     case XtPageOrientationSeascape:
  346.     ret_val.psx = gvw->ghostview.urx - 
  347.               event->xbutton.y * 72.0 / gvw->ghostview.ydpi;
  348.     ret_val.psy = gvw->ghostview.ury - 
  349.               event->xbutton.x * 72.0 / gvw->ghostview.xdpi;
  350.     break;
  351.     }
  352.     XtCallCallbackList(w, gvw->ghostview.callback, (XtPointer) &ret_val);
  353. }
  354.  
  355. #ifndef SEEK_SET
  356. #define SEEK_SET 0
  357. #endif
  358.  
  359. static Boolean broken_pipe = False;
  360.  
  361. static SIGVAL
  362. CatchPipe(i)
  363.     int i;
  364. {
  365.     broken_pipe = True;
  366. #ifdef SIGNALRETURNSINT
  367.     return 0;
  368. #endif
  369. }
  370.  
  371. #ifndef VMS
  372.  
  373. /* Input - Feed data to ghostscript's stdin.
  374.  * Write bytes to ghostscript using non-blocking I/O.
  375.  * Also, pipe signals are caught during writing.  The return
  376.  * values are checked and the appropriate action is taken.  I do
  377.  * this at this low level, because it may not be appropriate for
  378.  * SIGPIPE to be caught for the overall application.
  379.  */
  380.  
  381. static void
  382. Input(client_data, source, id)
  383.     XtPointer client_data;
  384.     int *source;
  385.     XtInputId *id;
  386. {
  387.     Widget w = (Widget) client_data;
  388.     GhostviewWidget gvw = (GhostviewWidget) w;
  389.     int bytes_written;
  390.     SIGVAL (*oldsig)();
  391.  
  392.     oldsig = signal(SIGPIPE, CatchPipe);
  393.  
  394. #ifdef NON_BLOCKING_IO
  395.     do {
  396. #endif
  397.  
  398.     if (gvw->ghostview.buffer_bytes_left == 0) {
  399.  
  400.         /* Get a new section if required */
  401.         if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
  402.         struct record_list *ps_old = gvw->ghostview.ps_input;
  403.         gvw->ghostview.ps_input = ps_old->next;
  404.         if (ps_old->close) fclose(ps_old->fp);
  405.         XtFree((char *)ps_old);
  406.         }
  407.  
  408.         /* Have to seek at the beginning of each section */
  409.         if (gvw->ghostview.ps_input &&
  410.         gvw->ghostview.ps_input->seek_needed) {
  411.         if (gvw->ghostview.ps_input->len > 0)
  412.             fseek(gvw->ghostview.ps_input->fp,
  413.               gvw->ghostview.ps_input->begin, SEEK_SET);
  414.         gvw->ghostview.ps_input->seek_needed = False;
  415.         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
  416.         }
  417.  
  418.         if (gvw->ghostview.bytes_left > GV_BUFSIZ) {
  419.         gvw->ghostview.buffer_bytes_left =
  420.             fread(gvw->ghostview.input_buffer,
  421.                   sizeof (char), GV_BUFSIZ,
  422.                   gvw->ghostview.ps_input->fp);
  423.         } else if (gvw->ghostview.bytes_left > 0) {
  424.         gvw->ghostview.buffer_bytes_left =
  425.             fread(gvw->ghostview.input_buffer,
  426.                   sizeof (char), gvw->ghostview.bytes_left,
  427.                   gvw->ghostview.ps_input->fp);
  428.         } else {
  429.         gvw->ghostview.buffer_bytes_left = 0;
  430.         }
  431.         if (gvw->ghostview.bytes_left > 0 &&
  432.         gvw->ghostview.buffer_bytes_left == 0) {
  433.         InterpreterFailed(w);    /* Error occurred */
  434.         }
  435.         gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
  436.         gvw->ghostview.bytes_left -= gvw->ghostview.buffer_bytes_left;
  437.     }
  438.  
  439.     if (gvw->ghostview.buffer_bytes_left > 0) {
  440.         bytes_written = write(gvw->ghostview.interpreter_input,
  441.                   gvw->ghostview.input_buffer_ptr,
  442.                   gvw->ghostview.buffer_bytes_left);
  443.  
  444.         if (broken_pipe) {
  445.         broken_pipe = False;
  446.         InterpreterFailed(w);        /* Something bad happened */
  447.         } else if (bytes_written == -1) {
  448.         if ((errno != EWOULDBLOCK) && (errno != EAGAIN)) {
  449.             InterpreterFailed(w);    /* Something bad happened */
  450.         }
  451.         } else {
  452.         gvw->ghostview.buffer_bytes_left -= bytes_written;
  453.         gvw->ghostview.input_buffer_ptr += bytes_written;
  454.         }
  455.     }
  456. #ifdef NON_BLOCKING_IO
  457.     } while(gvw->ghostview.ps_input &&
  458.         gvw->ghostview.buffer_bytes_left == 0);
  459. #endif
  460.     signal(SIGPIPE, oldsig);
  461.     if (gvw->ghostview.ps_input == NULL &&
  462.     gvw->ghostview.buffer_bytes_left == 0) {
  463.     if (gvw->ghostview.interpreter_input_id != None) {
  464.         XtRemoveInput(gvw->ghostview.interpreter_input_id);
  465.         gvw->ghostview.interpreter_input_id = None;
  466.     }
  467.     }
  468. }
  469.  
  470. /* Output - receive I/O from ghostscript's stdout and stderr.
  471.  * Pass this to the application via the output_callback. */
  472. static void
  473. Output(client_data, source, id)
  474.     XtPointer client_data;
  475.     int *source;
  476.     XtInputId *id;
  477. {
  478.     Widget w = (Widget) client_data;
  479.     GhostviewWidget gvw = (GhostviewWidget) w;
  480.     char buf[GV_BUFSIZ+1];
  481.     int bytes = 0;
  482.  
  483.     if (*source == gvw->ghostview.interpreter_output) {
  484.     bytes = read(gvw->ghostview.interpreter_output, buf, GV_BUFSIZ);
  485.     if (bytes == 0) { /* EOF occurred */
  486.         close(gvw->ghostview.interpreter_output);
  487.         gvw->ghostview.interpreter_output = -1;
  488.         XtRemoveInput(gvw->ghostview.interpreter_output_id);
  489.         return;
  490.     } else if (bytes == -1) {
  491.         InterpreterFailed(w);        /* Something bad happened */
  492.         return;
  493.     }
  494.     } else if (*source == gvw->ghostview.interpreter_error) {
  495.     bytes = read(gvw->ghostview.interpreter_error, buf, GV_BUFSIZ);
  496.     if (bytes == 0) { /* EOF occurred */
  497.         close(gvw->ghostview.interpreter_error);
  498.         gvw->ghostview.interpreter_error = -1;
  499.         XtRemoveInput(gvw->ghostview.interpreter_error_id);
  500.         return;
  501.     } else if (bytes == -1) {
  502.         InterpreterFailed(w);        /* Something bad happened */
  503.         return;
  504.     }
  505.     }
  506.     if (bytes > 0) {
  507.     buf[bytes] = '\0';
  508.     XtCallCallbackList(w, gvw->ghostview.output_callback, (XtPointer) buf);
  509.     }
  510. }
  511.  
  512. #endif /* VMS */
  513.  
  514. /* Register the type converter required for the PageOrientation. */
  515. /* This routine is called exactly once. */
  516. static void
  517. ClassInitialize()
  518. {
  519.     XtSetTypeConverter(XtRString, XtRPageOrientation,
  520.                XmuCvtStringToPageOrientation, NULL, 0,
  521.                XtCacheAll, NULL);
  522. }
  523.  
  524. /* Get atoms needed to communicate with ghostscript. */
  525. /* This routine is called once per display. */
  526. static void
  527. ClassPartInitialize(class)
  528.     WidgetClass class;
  529. {
  530.     GhostviewWidgetClass gvc = (GhostviewWidgetClass)class;
  531.     gvc->ghostview_class.ghostview = XmuMakeAtom("GHOSTVIEW");
  532.     gvc->ghostview_class.next = XmuMakeAtom("NEXT");
  533.     gvc->ghostview_class.page = XmuMakeAtom("PAGE");
  534.     gvc->ghostview_class.done = XmuMakeAtom("DONE");
  535. }
  536.  
  537. /* Initialize private state. */
  538.  
  539. static void
  540. Initialize(request, new, args, num_args)
  541.     Widget request, new;
  542.     ArgList args;        /* unused */
  543.     Cardinal *num_args;    /* unused */
  544. {
  545.     XGCValues    values;
  546.     XtGCMask    mask;
  547.     GhostviewWidget ngvw = (GhostviewWidget) new;
  548.     GhostviewWidget rgvw = (GhostviewWidget) request;
  549.  
  550.     values.foreground = new->core.background_pixel;
  551.     mask = GCForeground;
  552.     ngvw->ghostview.gc = XtGetGC(new, mask, &values);
  553.     ngvw->ghostview.mwin = None;
  554.     ngvw->ghostview.disable_start = False;
  555.     ngvw->ghostview.interpreter_pid = -1;
  556.     ngvw->ghostview.input_buffer = NULL;
  557.     ngvw->ghostview.bytes_left = 0;
  558. #ifndef VMS
  559.     ngvw->ghostview.input_buffer_ptr = NULL;
  560.     ngvw->ghostview.buffer_bytes_left = 0;
  561. #endif
  562.     ngvw->ghostview.ps_input = NULL;
  563.     ngvw->ghostview.interpreter_input = -1;
  564.     ngvw->ghostview.interpreter_output = -1;
  565. #ifndef VMS
  566.     ngvw->ghostview.interpreter_error = -1;
  567.     ngvw->ghostview.interpreter_input_id = None;
  568.     ngvw->ghostview.interpreter_output_id = None;
  569.     ngvw->ghostview.interpreter_error_id = None;
  570. #else /* VMS */
  571.     memset(ngvw->ghostview.interpreter_input_iosb, 0, 8);
  572.     memset(ngvw->ghostview.interpreter_output_iosb, 0, 8);
  573.     ngvw->ghostview.output_buffer = NULL;
  574. #endif /* VMS */
  575.     ngvw->ghostview.gs_width = 0;
  576.     ngvw->ghostview.gs_height = 0;
  577.     ngvw->ghostview.changed = False;
  578.     ngvw->ghostview.busy = False;
  579.  
  580.     /* Compute window size */
  581.     Layout(new, (rgvw->core.width == 0), (rgvw->core.height == 0));
  582. }
  583.  
  584. /* Create Window and start interpreter if needed */
  585. static void
  586. Realize(w, valueMask, attributes)
  587.     Widget w;
  588.     Mask *valueMask;
  589.     XSetWindowAttributes *attributes;
  590. {
  591.     GhostviewWidget gvw = (GhostviewWidget) w;
  592.  
  593.     if (gvw->ghostview.cursor != None) {
  594.     attributes->cursor = gvw->ghostview.cursor;
  595.     *valueMask |= CWCursor;
  596.     }
  597.  
  598.     XtCreateWindow(w, (unsigned int) InputOutput, (Visual *) CopyFromParent,
  599.            *valueMask, attributes);
  600.  
  601.     Setup(w);
  602. }
  603.  
  604. /* Destroy routine: kill the interpreter and release the GC */
  605. static void
  606. Destroy(w)
  607.     Widget w;
  608. {
  609.     GhostviewWidget gvw = (GhostviewWidget) w;
  610.  
  611.     StopInterpreter(w);
  612.     XtReleaseGC(w, gvw->ghostview.gc);
  613.     if (gvw->ghostview.input_buffer) XtFree(gvw->ghostview.input_buffer);
  614. #ifdef VMS
  615.     if (gvw->ghostview.output_buffer) XtFree(gvw->ghostview.output_buffer);
  616. #endif VMS
  617.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
  618.     XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  619. }
  620.  
  621. /* Process resize request.  Requested size cannot be changed.
  622.  * NOTE: This routine may be called before the widget is realized.
  623.  * (It was a surprise to me.)
  624.  * If the widget is realized, start a new interpreter by calling Setup().
  625.  * If Setup() actually started a new interpreter and it is taking input
  626.  * from stdin, send a refresh message to the application.  This is the
  627.  * only way that the application can be notified that it needs to resend
  628.  * the input because someone forced a new window size on the widget.
  629.  */
  630. static void
  631. Resize(w)
  632.     Widget w;
  633. {
  634.     Layout(w, False, False);
  635.     if (!XtIsRealized(w)) return;
  636.     if (Setup(w)) {
  637.     GhostviewWidget gvw = (GhostviewWidget) w;
  638.     if (gvw->ghostview.filename == NULL) {
  639.         XtCallCallbackList(w, gvw->ghostview.message_callback, "Refresh");
  640.     }
  641.     }
  642. }
  643.  
  644. /* SetValues routine.  Set new private state, based on changed values
  645.  * in the widget.  Always returns False, because redisplay is never needed.
  646.  */
  647. static Boolean
  648. SetValues(current, request, new)
  649.     Widget current, request, new;
  650. {
  651.     GhostviewWidget cgvw = (GhostviewWidget) current;
  652.     GhostviewWidget rgvw = (GhostviewWidget) request;
  653.     GhostviewWidget ngvw = (GhostviewWidget) new;
  654.     String cfilename;
  655.     String rfilename;
  656.     String cpreload;
  657.     String rpreload;
  658.  
  659.     cfilename = cgvw->ghostview.filename;
  660.     if (cfilename == NULL) cfilename = "(null)";
  661.     rfilename = rgvw->ghostview.filename;
  662.     if (rfilename == NULL) rfilename = "(null)";
  663.     cpreload = cgvw->ghostview.preload;
  664.     if (cpreload == NULL) cpreload = "(null)";
  665.     rpreload = rgvw->ghostview.preload;
  666.     if (rpreload == NULL) rpreload = "(null)";
  667.  
  668.     if (XtIsRealized(new) && !ngvw->ghostview.busy &&
  669.     (cgvw->ghostview.cursor != ngvw->ghostview.cursor)) {
  670.     XDefineCursor(XtDisplay(new), XtWindow(new), ngvw->ghostview.cursor);
  671.     }
  672.     if (XtIsRealized(new) && ngvw->ghostview.busy &&
  673.     (cgvw->ghostview.busy_cursor != ngvw->ghostview.busy_cursor)) {
  674.     XDefineCursor(XtDisplay(new), XtWindow(new),
  675.               ngvw->ghostview.busy_cursor);
  676.     }
  677.     if ((cgvw->core.width != rgvw->core.width) ||
  678.     (cgvw->core.height != rgvw->core.height) ||
  679.     strcmp(cgvw->ghostview.interpreter, rgvw->ghostview.interpreter) ||
  680.     strcmp(cpreload, rpreload) ||
  681.     (cgvw->ghostview.quiet != rgvw->ghostview.quiet) ||
  682.     strcmp(cfilename, rfilename) ||
  683.     (cgvw->ghostview.orientation != rgvw->ghostview.orientation) ||
  684.     (cgvw->ghostview.use_bpixmap != rgvw->ghostview.use_bpixmap) ||
  685.     (cgvw->ghostview.xdpi != rgvw->ghostview.xdpi) ||
  686.     (cgvw->ghostview.ydpi != rgvw->ghostview.ydpi) ||
  687.     (cgvw->ghostview.bottom_margin != rgvw->ghostview.bottom_margin) ||
  688.     (cgvw->ghostview.left_margin != rgvw->ghostview.left_margin) ||
  689.     (cgvw->ghostview.right_margin != rgvw->ghostview.right_margin) ||
  690.     (cgvw->ghostview.top_margin != rgvw->ghostview.top_margin) ||
  691.     (cgvw->ghostview.llx != rgvw->ghostview.llx) ||
  692.     (cgvw->ghostview.lly != rgvw->ghostview.lly) ||
  693.     (cgvw->ghostview.urx != rgvw->ghostview.urx) ||
  694.     (cgvw->ghostview.ury != rgvw->ghostview.ury)) {
  695.  
  696.     ngvw->ghostview.changed = True;
  697.     Layout(new, True, True);
  698.     }
  699.  
  700.     if (ngvw->ghostview.changed && XtIsRealized(current)) Setup(new);
  701.     return(False);
  702. }
  703.  
  704. /*    Function Name: QueryGeometry
  705.  *    Description: This tells the parent what size we would like to be
  706.  *                   given certain constraints.
  707.  *    Arguments: w - the widget.
  708.  *                 intended - what the parent intends to do with us.
  709.  *                 requested - what we want to happen.
  710.  */
  711.  
  712. static XtGeometryResult 
  713. QueryGeometry(w, intended, requested)
  714. Widget w;
  715. XtWidgetGeometry *intended, *requested;
  716. {
  717.     Dimension new_width, new_height;
  718.     Boolean change, width_req, height_req;
  719.     
  720.     width_req = intended->request_mode & CWWidth;
  721.     height_req = intended->request_mode & CWHeight;
  722.  
  723.     if (width_req)
  724.       new_width = intended->width;
  725.     else
  726.       new_width = w->core.width;
  727.  
  728.     if (height_req)
  729.       new_height = intended->height;
  730.     else
  731.       new_height = w->core.height;
  732.  
  733.     requested->request_mode = 0;
  734.     
  735. /*
  736.  * We only care about our height and width.
  737.  */
  738.  
  739.     if (!width_req && !height_req)
  740.       return(XtGeometryYes);
  741.     
  742.     change = ComputeSize(w, !width_req, !height_req, &new_width, &new_height);
  743.  
  744.     requested->request_mode |= CWWidth;
  745.     requested->width = new_width;
  746.     requested->request_mode |= CWHeight;
  747.     requested->height = new_height;
  748.  
  749.     if (change)
  750.         return(XtGeometryAlmost);
  751.     return(XtGeometryYes);
  752. }
  753.  
  754. /* Layout the widget. */
  755.  
  756. static void
  757. Layout(w, xfree, yfree)
  758.     Widget w;
  759.     Boolean xfree, yfree;
  760. {
  761.     Dimension width = w->core.width;
  762.     Dimension height = w->core.height;
  763.     Boolean different_size = ComputeSize(w, xfree, yfree, &width, &height);
  764.     if (different_size) ChangeSize(w, width, height);
  765. }
  766.  
  767. /* Compute new size of window, sets xdpi and ydpi if necessary.
  768.  * returns True if new window size is different */
  769. static Boolean
  770. ComputeSize(w, xfree, yfree, width, height)
  771.     Widget w;
  772.     Boolean xfree, yfree;    /* Am I allowed to change width or height */
  773.     Dimension *width, *height;
  774. {
  775.     GhostviewWidget gvw = (GhostviewWidget) w;
  776.     Dimension new_width = *width;
  777.     Dimension new_height = *height;
  778.     float newxdpi, newydpi;
  779.     Boolean change;
  780.  
  781.     if (xfree && yfree) {
  782.     /* width and height can be changed, calculate window size according */
  783.     /* to xpdi and ydpi */
  784.     switch (gvw->ghostview.orientation) {
  785.     case XtPageOrientationPortrait:
  786.     case XtPageOrientationUpsideDown:
  787.         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  788.              gvw->ghostview.xdpi + 0.5;
  789.         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  790.               gvw->ghostview.ydpi + 0.5;
  791.         break;
  792.     case XtPageOrientationLandscape:
  793.     case XtPageOrientationSeascape:
  794.         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  795.              gvw->ghostview.xdpi + 0.5;
  796.         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  797.               gvw->ghostview.ydpi + 0.5;
  798.         break;
  799.     }
  800.     } else if (xfree) {
  801.     /* height is fixed.  Preserve aspect ratio by recomputing */
  802.     /* ydpi and xdpi */
  803.     switch (gvw->ghostview.orientation) {
  804.     case XtPageOrientationPortrait:
  805.     case XtPageOrientationUpsideDown:
  806.         newydpi = gvw->core.height * 72.0 /
  807.               (gvw->ghostview.ury - gvw->ghostview.lly);
  808.         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  809.         gvw->ghostview.xdpi = newxdpi;
  810.         gvw->ghostview.ydpi = newydpi;
  811.         new_width = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  812.              gvw->ghostview.xdpi + 0.5;
  813.         break;
  814.     case XtPageOrientationLandscape:
  815.     case XtPageOrientationSeascape:
  816.         newydpi = gvw->core.height * 72.0 /
  817.               (gvw->ghostview.urx - gvw->ghostview.llx);
  818.         newxdpi = newydpi * gvw->ghostview.xdpi / gvw->ghostview.ydpi;
  819.         gvw->ghostview.xdpi = newxdpi;
  820.         gvw->ghostview.ydpi = newydpi;
  821.         new_width = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  822.              gvw->ghostview.xdpi + 0.5;
  823.         break;
  824.     }
  825.     } else if (yfree) {
  826.     /* width is fixed.  Preserve aspect ratio by recomputing */
  827.     /* xdpi and ydpi */
  828.     switch (gvw->ghostview.orientation) {
  829.     case XtPageOrientationPortrait:
  830.     case XtPageOrientationUpsideDown:
  831.         newxdpi = gvw->core.width * 72.0 /
  832.               (gvw->ghostview.urx - gvw->ghostview.llx);
  833.         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  834.         gvw->ghostview.xdpi = newxdpi;
  835.         gvw->ghostview.ydpi = newydpi;
  836.         new_height = (gvw->ghostview.ury - gvw->ghostview.lly) / 72.0 *
  837.               gvw->ghostview.ydpi + 0.5;
  838.         break;
  839.     case XtPageOrientationLandscape:
  840.     case XtPageOrientationSeascape:
  841.         newxdpi = gvw->core.width * 72.0 /
  842.               (gvw->ghostview.ury - gvw->ghostview.lly);
  843.         newydpi = newxdpi * gvw->ghostview.ydpi / gvw->ghostview.xdpi;
  844.         gvw->ghostview.xdpi = newxdpi;
  845.         gvw->ghostview.ydpi = newydpi;
  846.         new_height = (gvw->ghostview.urx - gvw->ghostview.llx) / 72.0 *
  847.               gvw->ghostview.ydpi + 0.5;
  848.         break;
  849.     }
  850.     } else {
  851.     /* height and width are fixed.  Just have to live with it. */
  852.     switch (gvw->ghostview.orientation) {
  853.     case XtPageOrientationPortrait:
  854.     case XtPageOrientationUpsideDown:
  855.         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  856.                   (gvw->ghostview.urx - gvw->ghostview.llx);
  857.         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  858.                   (gvw->ghostview.ury - gvw->ghostview.lly);
  859.         break;
  860.     case XtPageOrientationLandscape:
  861.     case XtPageOrientationSeascape:
  862.         gvw->ghostview.xdpi = gvw->core.width * 72.0 /
  863.                   (gvw->ghostview.ury - gvw->ghostview.lly);
  864.         gvw->ghostview.ydpi = gvw->core.height * 72.0 /
  865.                   (gvw->ghostview.urx - gvw->ghostview.llx);
  866.         break;
  867.     }
  868.     }
  869.  
  870.     change = (new_width != *width) || (new_height != *height);
  871.     *width = new_width;
  872.     *height = new_height;
  873.     return (change);
  874. }
  875.  
  876. /*    Function Name: ChangeSize.
  877.  *    Description: Request a size change.
  878.  *    Arguments: w - the widget to try change the size of.
  879.  */
  880.  
  881. static void
  882. ChangeSize(w, width, height)
  883. Widget w;
  884. Dimension width, height;
  885. {
  886.     XtWidgetGeometry request, reply;
  887.     Boolean changed = False;
  888.  
  889.     request.request_mode = CWWidth | CWHeight;
  890.     request.width = width;
  891.     request.height = height;
  892.     
  893.     switch ( XtMakeGeometryRequest(w, &request, &reply) ) {
  894.     case XtGeometryYes:
  895.     changed = True;
  896.         break;
  897.     case XtGeometryNo:
  898.         break;
  899.     case XtGeometryAlmost:
  900.     ComputeSize(w, (request.height != reply.height),
  901.                (request.width != reply.width),
  902.                &(reply.width), &(reply.height));
  903.     request = reply;
  904.     switch (XtMakeGeometryRequest(w, &request, &reply) ) {
  905.     case XtGeometryYes:
  906.         changed = True;
  907.         break;
  908.     case XtGeometryNo:
  909.         break;
  910.     case XtGeometryAlmost:
  911.         request = reply;
  912.         ComputeSize(w, FALSE, FALSE, &(request.width), &(request.height));
  913.         request.request_mode = CWWidth | CWHeight;
  914.         XtMakeGeometryRequest(w, &request, &reply);
  915.         changed = True;
  916.         break;
  917.     }
  918.     break;
  919.     }
  920.  
  921.     /* If success, setup the widet for the new size. */
  922.     if (changed && XtIsRealized(w)) Setup(w);
  923. }
  924.  
  925. /* Catch the alloc error when there is not enough resources for the
  926.  * backing pixmap.  Automatically shut off backing pixmap and let the
  927.  * user know when this happens.
  928.  */
  929. static Boolean alloc_error;
  930. static XErrorHandler oldhandler;
  931.  
  932. static int
  933. catch_alloc (dpy, err)
  934. Display *dpy;
  935. XErrorEvent *err;
  936. {
  937.     if (err->error_code == BadAlloc) {
  938.     alloc_error = True;
  939.     }
  940.     if (alloc_error) return 0;
  941.     oldhandler(dpy, err);
  942. }
  943.  
  944. /* Setup - sets up the backing pixmap, and GHOSTVIEW property and
  945.  * starts interpreter if needed.
  946.  * NOTE: the widget must be realized before calling Setup().
  947.  * Returns True if a new interpreter was started, False otherwise.
  948.  */
  949.  
  950. static Boolean
  951. Setup(w)
  952.     Widget w;
  953. {
  954.     GhostviewWidget gvw = (GhostviewWidget) w;
  955.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  956.     char buf[GV_BUFSIZ];
  957.     Pixmap bpixmap;
  958.     XSetWindowAttributes xswa;
  959.  
  960.     if (!gvw->ghostview.changed &&
  961.     (gvw->core.width == gvw->ghostview.gs_width) &&
  962.     (gvw->core.height == gvw->ghostview.gs_height)) return False;
  963.  
  964.     StopInterpreter(w);
  965.  
  966.     if ((gvw->core.width != gvw->ghostview.gs_width) ||
  967.     (gvw->core.height != gvw->ghostview.gs_height) ||
  968.     (!gvw->ghostview.use_bpixmap)) {
  969.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  970.         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  971.         gvw->core.background_pixmap = XtUnspecifiedPixmap;
  972.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
  973.     }
  974.     }
  975.  
  976.     if (gvw->ghostview.use_bpixmap) {
  977.     if (gvw->core.background_pixmap == XtUnspecifiedPixmap) {
  978.         /* Get a Backing Pixmap, but be ready for the BadAlloc. */
  979.         XSync(XtDisplay(w), False);  /* Get to known state */
  980.         oldhandler = XSetErrorHandler(catch_alloc);
  981.         alloc_error = False;
  982.         bpixmap = XCreatePixmap(XtDisplay(w), XtWindow(w),
  983.                     gvw->core.width, gvw->core.height,
  984.                     gvw->core.depth);
  985.         XSync(XtDisplay(w), False);  /* Force the error */
  986.         if (alloc_error) {
  987.         XtCallCallbackList(w, gvw->ghostview.message_callback,
  988.                    "BadAlloc");
  989.         if (bpixmap != None) {
  990.             XFreePixmap(XtDisplay(w), bpixmap);
  991.             XSync(XtDisplay(w), False);  /* Force the error */
  992.             bpixmap = None;
  993.         }
  994.         }
  995.         oldhandler = XSetErrorHandler(oldhandler);
  996.         if (bpixmap != None) {
  997.         gvw->core.background_pixmap = bpixmap;
  998.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w),
  999.                        gvw->core.background_pixmap);
  1000.         }
  1001.     } else {
  1002.         bpixmap = gvw->core.background_pixmap;
  1003.     }
  1004.     } else {
  1005.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1006.         XFreePixmap(XtDisplay(w), gvw->core.background_pixmap);
  1007.         gvw->core.background_pixmap = XtUnspecifiedPixmap;
  1008.         XSetWindowBackgroundPixmap(XtDisplay(w), XtWindow(w), None);
  1009.     }
  1010.     bpixmap = None;
  1011.     }
  1012.  
  1013.     if (bpixmap != None) {
  1014.     xswa.backing_store = NotUseful;
  1015.     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
  1016.                 CWBackingStore, &xswa);
  1017.     } else {
  1018.     xswa.backing_store = Always;
  1019.     XChangeWindowAttributes(XtDisplay(w), XtWindow(w),
  1020.                 CWBackingStore, &xswa);
  1021.     }
  1022.  
  1023.     gvw->ghostview.gs_width = gvw->core.width;
  1024.     gvw->ghostview.gs_height = gvw->core.height;
  1025.  
  1026.     sprintf(buf, "%d %d %d %d %d %d %g %g %d %d %d %d",
  1027.         bpixmap, gvw->ghostview.orientation,
  1028.         gvw->ghostview.llx, gvw->ghostview.lly,
  1029.         gvw->ghostview.urx, gvw->ghostview.ury,
  1030.         gvw->ghostview.xdpi, gvw->ghostview.ydpi,
  1031.         gvw->ghostview.left_margin, gvw->ghostview.bottom_margin,
  1032.         gvw->ghostview.right_margin, gvw->ghostview.top_margin);
  1033.     XChangeProperty(XtDisplay(w), XtWindow(w),
  1034.            XmuInternAtom(XtDisplay(w), gvc->ghostview_class.ghostview),
  1035.            XA_STRING, 8, PropModeReplace,
  1036.            (unsigned char *)buf, strlen(buf));
  1037.  
  1038.     StartInterpreter(w);
  1039.     return True;
  1040. }
  1041.  
  1042. #ifndef VMS
  1043.  
  1044. /* This routine starts the interpreter.  It sets the DISPLAY and 
  1045.  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
  1046.  * contains the Window that ghostscript should write on.
  1047.  *
  1048.  * This routine also opens pipes for stdout and stderr and initializes
  1049.  * application input events for them.  If input to ghostscript is not
  1050.  * from a file, a pipe for stdin is created.  This pipe is setup for
  1051.  * non-blocking I/O so that the user interface never "hangs" because of
  1052.  * a write to ghostscript.
  1053.  */
  1054. static void
  1055. StartInterpreter(w)
  1056.     Widget w;
  1057. {
  1058.     GhostviewWidget gvw = (GhostviewWidget) w;
  1059.     int    std_in[2];
  1060.     int    std_out[2];
  1061.     int    std_err[2];
  1062.     char buf[GV_BUFSIZ];
  1063. #define NUM_ARGS 100
  1064.     char *argv[NUM_ARGS];
  1065.     char *preload = NULL;
  1066.     char *cptr;
  1067.     int argc = 0;
  1068.     int ret;
  1069.  
  1070.     StopInterpreter(w);
  1071.  
  1072.     /* Clear the window before starting a new interpreter. */
  1073.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1074.     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
  1075.                gvw->ghostview.gc,
  1076.                0, 0, gvw->core.width, gvw->core.height);
  1077.     }
  1078.     XClearArea(XtDisplay(w), XtWindow(w),
  1079.            0, 0, gvw->core.width, gvw->core.height, False);
  1080.  
  1081.     if (gvw->ghostview.disable_start) return;
  1082.  
  1083.     argv[argc++] = gvw->ghostview.interpreter;
  1084.     if (gvw->ghostview.quiet) argv[argc++] = "-dQUIET";
  1085.     argv[argc++] = "-dNOPAUSE";
  1086.     if (gvw->ghostview.preload) {
  1087.     cptr = preload = XtNewString(gvw->ghostview.preload);
  1088.     while (isspace(*cptr)) cptr++;
  1089.     while (*cptr) {
  1090.         argv[argc++] = cptr;
  1091.         while (*cptr && !isspace(*cptr)) cptr++;
  1092.         *cptr++ = '\0';
  1093.         if (argc + 2 >= NUM_ARGS) {
  1094.         fprintf(stderr, "Too many files to preload.\n");
  1095.         exit(1);
  1096.         }
  1097.         while (isspace(*cptr)) cptr++;
  1098.     }
  1099.     }
  1100.     argv[argc++] = "-";
  1101.     argv[argc++] = NULL;
  1102.  
  1103.     if (gvw->ghostview.filename == NULL) {
  1104.     ret = pipe(std_in);
  1105.     if (ret == -1) {
  1106.         perror("Could not create pipe");
  1107.         exit(1);
  1108.     }
  1109.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1110.     std_in[0] = open(gvw->ghostview.filename, O_RDONLY, 0);
  1111.     }
  1112.     ret = pipe(std_out);
  1113.     if (ret == -1) {
  1114.     perror("Could not create pipe");
  1115.     exit(1);
  1116.     }
  1117.     ret = pipe(std_err);
  1118.     if (ret == -1) {
  1119.     perror("Could not create pipe");
  1120.     exit(1);
  1121.     }
  1122.  
  1123.     gvw->ghostview.changed = False;
  1124.     gvw->ghostview.busy = True;
  1125.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
  1126. #if defined(SYSV) || defined(USG)
  1127. #define vfork fork
  1128. #endif
  1129.     gvw->ghostview.interpreter_pid = vfork();
  1130.  
  1131.     if (gvw->ghostview.interpreter_pid == 0) { /* child */
  1132.     close(std_out[0]);
  1133.     close(std_err[0]);
  1134.     dup2(std_out[1], 1);
  1135.     close(std_out[1]);
  1136.     dup2(std_err[1], 2);
  1137.     close(std_err[1]);
  1138.     sprintf(buf, "%d", XtWindow(w));
  1139.     setenv("GHOSTVIEW", buf, True);
  1140.     setenv("DISPLAY", XDisplayString(XtDisplay(w)), True);
  1141.     if (gvw->ghostview.filename == NULL) {
  1142.         close(std_in[1]);
  1143.         dup2(std_in[0], 0);
  1144.         close(std_in[0]);
  1145.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1146.         dup2(std_in[0], 0);
  1147.         close(std_in[0]);
  1148.     }
  1149.     execvp(argv[0], argv);
  1150.     sprintf(buf, "Exec of %s failed", argv[0]);
  1151.     perror(buf);
  1152.     _exit(1);
  1153.     } else {
  1154.     if (gvw->ghostview.filename == NULL) {
  1155. #ifdef NON_BLOCKING_IO
  1156.         int result;
  1157. #endif
  1158.         close(std_in[0]);
  1159.  
  1160. #ifdef NON_BLOCKING_IO
  1161.         result = fcntl(std_in[1], F_GETFL, 0);
  1162.         result = result | O_NONBLOCK;
  1163.         result = fcntl(std_in[1], F_SETFL, result);
  1164. #endif
  1165.         gvw->ghostview.interpreter_input = std_in[1];
  1166.         gvw->ghostview.interpreter_input_id = None;
  1167.     } else if (strcmp(gvw->ghostview.filename, "-")) {
  1168.         close(std_in[0]);
  1169.     }
  1170.     close(std_out[1]);
  1171.     gvw->ghostview.interpreter_output = std_out[0];
  1172.     gvw->ghostview.interpreter_output_id = 
  1173.         XtAppAddInput(XtWidgetToApplicationContext(w), std_out[0],
  1174.               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
  1175.     close(std_err[1]);
  1176.     gvw->ghostview.interpreter_error = std_err[0];
  1177.     gvw->ghostview.interpreter_error_id = 
  1178.         XtAppAddInput(XtWidgetToApplicationContext(w), std_err[0],
  1179.               (XtPointer)XtInputReadMask, Output, (XtPointer)w);
  1180.     }
  1181.     if (preload) XtFree(preload);
  1182. }
  1183.  
  1184. /* Stop the interperter, if present, and remove any Input sources. */
  1185. /* Also reset the busy state. */
  1186. static void
  1187. StopInterpreter(w)
  1188.     Widget w;
  1189. {
  1190.     GhostviewWidget gvw = (GhostviewWidget) w;
  1191.     if (gvw->ghostview.interpreter_pid >= 0) {
  1192.     kill(gvw->ghostview.interpreter_pid, SIGTERM);
  1193.     wait(0);
  1194.     gvw->ghostview.interpreter_pid = -1;
  1195.     }
  1196.     if (gvw->ghostview.interpreter_input >= 0) {
  1197.     close(gvw->ghostview.interpreter_input);
  1198.     gvw->ghostview.interpreter_input = -1;
  1199.     if (gvw->ghostview.interpreter_input_id != None) {
  1200.         XtRemoveInput(gvw->ghostview.interpreter_input_id);
  1201.         gvw->ghostview.interpreter_input_id = None;
  1202.     }
  1203.     while (gvw->ghostview.ps_input) {
  1204.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1205.         gvw->ghostview.ps_input = ps_old->next;
  1206.         if (ps_old->close) fclose(ps_old->fp);
  1207.         XtFree((char *)ps_old);
  1208.     }
  1209.     }
  1210.     if (gvw->ghostview.interpreter_output >= 0) {
  1211.     close(gvw->ghostview.interpreter_output);
  1212.     gvw->ghostview.interpreter_output = -1;
  1213.     XtRemoveInput(gvw->ghostview.interpreter_output_id);
  1214.     }
  1215.     if (gvw->ghostview.interpreter_error >= 0) {
  1216.     close(gvw->ghostview.interpreter_error);
  1217.     gvw->ghostview.interpreter_error = -1;
  1218.     XtRemoveInput(gvw->ghostview.interpreter_error_id);
  1219.     }
  1220.     gvw->ghostview.busy = False;
  1221.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  1222. }
  1223.  
  1224. #endif /* VMS */
  1225.  
  1226. /* The interpeter failed, Stop what's left and notify application */
  1227. static void
  1228. InterpreterFailed(w)
  1229.     Widget w;
  1230. {
  1231.     GhostviewWidget gvw = (GhostviewWidget) w;
  1232.     StopInterpreter(w);
  1233.     XtCallCallbackList(w, gvw->ghostview.message_callback, "Failed");
  1234. }
  1235.  
  1236. /*
  1237.  *    Public Routines
  1238.  */
  1239.  
  1240. /* GhostviewDisableInterpreter:
  1241.  * Stop any interpreter and disable new ones from starting.
  1242.  */
  1243. void
  1244. GhostviewDisableInterpreter(w)
  1245.     Widget w;
  1246. {
  1247.     GhostviewWidget gvw = (GhostviewWidget) w;
  1248.     gvw->ghostview.disable_start = True;
  1249.     if (XtIsRealized(w)) StopInterpreter(w);
  1250. }
  1251.  
  1252. /* GhostviewDisableInterpreter:
  1253.  * Allow an interpreter to start and start one if the widget is
  1254.  * currently realized.
  1255.  */
  1256. void
  1257. GhostviewEnableInterpreter(w)
  1258.     Widget w;
  1259. {
  1260.     GhostviewWidget gvw = (GhostviewWidget) w;
  1261.     gvw->ghostview.disable_start = False;
  1262.     if (XtIsRealized(w)) StartInterpreter(w);
  1263. }
  1264.  
  1265. /* GhostviewIsInterpreterReady:
  1266.  * Returns True if the interpreter is ready for new input.
  1267.  */
  1268. Boolean
  1269. GhostviewIsInterpreterReady(w)
  1270.     Widget w;
  1271. {
  1272.     GhostviewWidget gvw = (GhostviewWidget) w;
  1273.     return gvw->ghostview.interpreter_pid != -1 &&
  1274.        !gvw->ghostview.busy &&
  1275.        gvw->ghostview.ps_input == NULL;
  1276. }
  1277.  
  1278. /* GhostviewIsInterpreterRunning:
  1279.  * Returns True if the interpreter is running.
  1280.  */
  1281. Boolean
  1282. GhostviewIsInterpreterRunning(w)
  1283.     Widget w;
  1284. {
  1285.     GhostviewWidget gvw = (GhostviewWidget) w;
  1286.     return gvw->ghostview.interpreter_pid != -1;
  1287. }
  1288.  
  1289. /* GhostviewGetBackingPixmap:
  1290.  * Returns the current backing pixmap.
  1291.  */
  1292. Pixmap
  1293. GhostviewGetBackingPixmap(w)
  1294.     Widget w;
  1295. {
  1296.     GhostviewWidget gvw = (GhostviewWidget) w;
  1297.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap)
  1298.     return(gvw->core.background_pixmap);
  1299.     else
  1300.     return(None);
  1301. }
  1302.  
  1303. #ifndef VMS
  1304.  
  1305. /* GhostviewSendPS:
  1306.  *   Queue a portion of a PostScript file for output to ghostscript.
  1307.  *   fp: FILE * of the file in question.  NOTE: if you have several
  1308.  *   Ghostview widgets reading from the same file.  You must open
  1309.  *   a unique FILE * for each widget.
  1310.  *   SendPS does not actually send the PostScript, it merely queues it
  1311.  *   for output.
  1312.  *   begin: position in file (returned from ftell()) to start.
  1313.  *   len:   number of bytes to write.
  1314.  *
  1315.  *   If an interpreter is not running, nothing is queued and
  1316.  *   False is returned.
  1317.  */
  1318. Boolean
  1319. GhostviewSendPS(w, fp, begin, len, close)
  1320.     Widget w;
  1321.     FILE *fp;
  1322.     long begin;
  1323.     unsigned int len;
  1324.     Bool close;
  1325. {
  1326.     GhostviewWidget gvw = (GhostviewWidget) w;
  1327.     struct record_list *ps_new;
  1328.  
  1329.     if (gvw->ghostview.interpreter_input < 0) return False;
  1330.     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
  1331.     ps_new->fp = fp;
  1332.     ps_new->begin = begin;
  1333.     ps_new->len = len;
  1334.     ps_new->seek_needed = True;
  1335.     ps_new->close = close;
  1336.     ps_new->next = NULL;
  1337.  
  1338.     if (gvw->ghostview.input_buffer == NULL) {
  1339.     gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
  1340.     }
  1341.  
  1342.     if (gvw->ghostview.ps_input == NULL) {
  1343.     gvw->ghostview.input_buffer_ptr = gvw->ghostview.input_buffer;
  1344.     gvw->ghostview.bytes_left = len;
  1345.     gvw->ghostview.buffer_bytes_left = 0;
  1346.     gvw->ghostview.ps_input = ps_new;
  1347.     gvw->ghostview.interpreter_input_id =
  1348.         XtAppAddInput(XtWidgetToApplicationContext(w),
  1349.                   gvw->ghostview.interpreter_input,
  1350.                   (XtPointer)XtInputWriteMask, Input, (XtPointer)w);
  1351.     } else {
  1352.     struct record_list *p = gvw->ghostview.ps_input;
  1353.     while (p->next != NULL) {
  1354.         p = p->next;
  1355.     }
  1356.     p->next = ps_new;
  1357.     }
  1358.     return True;
  1359. }
  1360.  
  1361. #endif /* VMS */
  1362.  
  1363. /* GhostviewNextPage:
  1364.  *   Tell ghostscript to start the next page.
  1365.  *   Returns False if ghostscript is not running, or not ready to start
  1366.  *   another page.
  1367.  *   If another page is started.  Sets the busy flag and cursor.
  1368.  */
  1369. Boolean
  1370. GhostviewNextPage(w)
  1371.     Widget w;
  1372. {
  1373.     GhostviewWidget gvw = (GhostviewWidget) w;
  1374.     GhostviewWidgetClass gvc = (GhostviewWidgetClass) XtClass(w);
  1375.     XEvent event;
  1376.  
  1377.     if (gvw->ghostview.interpreter_pid < 0) return False;
  1378.     if (gvw->ghostview.mwin == None) return False;
  1379.  
  1380.     if (!gvw->ghostview.busy) {
  1381.     gvw->ghostview.busy = True;
  1382.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw),
  1383.               gvw->ghostview.busy_cursor);
  1384.  
  1385.     event.xclient.type = ClientMessage;
  1386.     event.xclient.display = XtDisplay(w);
  1387.     event.xclient.window = gvw->ghostview.mwin;
  1388.     event.xclient.message_type =
  1389.         XmuInternAtom(XtDisplay(w), gvc->ghostview_class.next);
  1390.     event.xclient.format = 32;
  1391.     XSendEvent(XtDisplay(w), gvw->ghostview.mwin, False, 0, &event);
  1392.     return True;
  1393.     } else {
  1394.     return False;
  1395.     }
  1396. }
  1397.  
  1398. #define    done(type, value) \
  1399.     {                            \
  1400.         if (toVal->addr != NULL) {                \
  1401.         if (toVal->size < sizeof(type)) {        \
  1402.             toVal->size = sizeof(type);            \
  1403.             return False;                \
  1404.         }                        \
  1405.         *(type*)(toVal->addr) = (value);        \
  1406.         }                            \
  1407.         else {                        \
  1408.         static type static_val;                \
  1409.         static_val = (value);                \
  1410.         toVal->addr = (XPointer)&static_val;        \
  1411.         }                            \
  1412.         toVal->size = sizeof(type);                \
  1413.         return True;                    \
  1414.     }
  1415.  
  1416. /* PageOrienation Conversion Routine.
  1417.  * Returns True if Conversion is successful.
  1418.  */
  1419. Boolean
  1420. XmuCvtStringToPageOrientation(dpy, args, num_args, fromVal, toVal, data)
  1421.     Display    *dpy;
  1422.     XrmValue    *args;        /* unused */
  1423.     Cardinal    *num_args;    /* unused */
  1424.     XrmValue    *fromVal;
  1425.     XrmValue    *toVal;
  1426.     XtPointer    *data;        /* unused */
  1427. {
  1428.     static XrmQuark        XrmQEportrait;
  1429.     static XrmQuark        XrmQElandscape;
  1430.     static XrmQuark        XrmQEupsideDown;
  1431.     static XrmQuark        XrmQEseascape;
  1432.     static int            haveQuarks;
  1433.     XrmQuark    q;
  1434.     char    *str = (XPointer) fromVal->addr;
  1435.     char        lowerName[1000];
  1436.  
  1437.     if (str == NULL) return False;
  1438.  
  1439.     if (!haveQuarks) {
  1440.     XrmQEportrait   = XrmStringToQuark(XtEportrait);
  1441.     XrmQElandscape  = XrmStringToQuark(XtElandscape);
  1442.     XrmQEupsideDown = XrmStringToQuark(XtEupsideDown);
  1443.     XrmQEseascape   = XrmStringToQuark(XtEseascape);
  1444.     haveQuarks = 1;
  1445.     }
  1446.  
  1447.     XmuCopyISOLatin1Lowered(lowerName, str);
  1448.  
  1449.     q = XrmStringToQuark(lowerName);
  1450.  
  1451.     if (q == XrmQEportrait)
  1452.     done(XtPageOrientation, XtPageOrientationPortrait);
  1453.     if (q == XrmQElandscape)
  1454.     done(XtPageOrientation, XtPageOrientationLandscape);
  1455.     if (q == XrmQEupsideDown)
  1456.     done(XtPageOrientation, XtPageOrientationUpsideDown);
  1457.     if (q == XrmQEseascape)
  1458.     done(XtPageOrientation, XtPageOrientationSeascape);
  1459.  
  1460.     XtDisplayStringConversionWarning(dpy, str, XtRPageOrientation);
  1461.     return False;
  1462. }
  1463.  
  1464. #ifdef VMS
  1465.  
  1466. /*
  1467. ** VMS specific include files
  1468. */
  1469. #include <descrip.h>
  1470. #include <ssdef.h>
  1471. #include <clidef.h>
  1472. #include <lnmdef.h>
  1473. #include <iodef.h>
  1474. #include <dvidef.h>
  1475. #include "vms_types.h"
  1476.  
  1477. #define ERR_SIGNAL(s) if(!((s) & 1))lib$signal((s), 0, 0)
  1478. #define XtEFN 23
  1479.  
  1480. struct g_l_i
  1481. {
  1482.     GhostviewWidget w;
  1483.     struct g_l_i *next;
  1484. };
  1485.  
  1486. typedef struct g_l_i GhostListItem, *GLI_p;
  1487.  
  1488. static GhostListItem glhead = {(GhostviewWidget) -1, NULL};
  1489. static GLI_p GL = &glhead;
  1490. static size_t GLI_Size = sizeof(GhostListItem);
  1491. static XtInputId EventId;
  1492.  
  1493. /*
  1494. ** This routine is passed to XtAppAddInput(). It is called whenever the event
  1495. ** flag number XtEFN is set and the Xt main loop becomes idle. It clears the
  1496. ** event flag and then scans all the ghostview widgets for completed I/O
  1497. ** requests, processing each as they are found. We have to do them all because
  1498. ** there is no way to have Xt dispatch them individually without a window of
  1499. ** vulnerability that can cause missed events, or by using a separate event
  1500. ** flag for each I/O stream. Event flags are, unfortunately, a limited
  1501. ** resource.
  1502. */
  1503. static Boolean
  1504. IOProcess()
  1505. {
  1506.     GhostviewWidget gvw;
  1507.     GLI_p cur;
  1508.  
  1509.     /*
  1510.     ** Before we process any I/O's, clear the event flag.
  1511.     */
  1512.     sys$clref(XtEFN);
  1513.     /*
  1514.     ** Scan all the ghostview widgets and check for completed I/O's
  1515.     */
  1516.     for(cur = GL->next; cur; cur = cur->next){
  1517.     /*
  1518.     ** Get the widget and check for I/O complete on either mailbox.
  1519.     */
  1520.     gvw = cur->w;
  1521.     if(gvw->ghostview.interpreter_input_iosb[0])Input(gvw);
  1522.     if(gvw->ghostview.interpreter_output_iosb[0])Output(gvw);
  1523.     }
  1524. }
  1525.     
  1526. /*
  1527. ** This is an AST routine. It is called asynchronously whenever one of our
  1528. ** mailbox I/O's completes.
  1529. */
  1530. static void
  1531. IOComplete(client_data)
  1532.     XtPointer client_data;
  1533. {
  1534.     /*
  1535.     ** Set the event flag to tell Xt to call IOProcess.
  1536.     */
  1537.     sys$setef(XtEFN);
  1538. }
  1539.  
  1540. static void
  1541. GLInsert(w)
  1542.     GhostviewWidget w;
  1543. {
  1544.     GLI_p new;
  1545.     int first;
  1546.     
  1547.     /*
  1548.     ** Insert this widget after the list head
  1549.     */
  1550.     first = (GL->next == NULL);
  1551.     new = XtMalloc(GLI_Size);
  1552.     new->w = w;
  1553.     new->next = GL->next;
  1554.     GL->next = new;
  1555.     /*
  1556.     ** If this is the first item on the list, call XtAppAddInput()
  1557.     */
  1558.     if(first)EventId = XtAppAddInput(XtWidgetToApplicationContext(w), XtEFN, 0, 
  1559.     IOProcess, 0);
  1560. }
  1561.  
  1562. static void
  1563. GLRemove(w)
  1564.     GhostviewWidget w;
  1565. {
  1566.     GLI_p prev, cur;
  1567.     int last = 0;
  1568.  
  1569.     /*
  1570.     ** Find and remove this widget from the list.
  1571.     */
  1572.     prev = GL;
  1573.     cur = prev->next;
  1574.     while(cur && cur->w != w){
  1575.     prev = cur;
  1576.     cur = cur->next;
  1577.     }
  1578.     if(cur){
  1579.        prev->next = cur->next;
  1580.        XtFree(cur);
  1581.        last = (GL->next == NULL);
  1582.     }
  1583.     /*
  1584.     ** If this was the last item on the list, call XtRemoveInput()
  1585.     */
  1586.     if(last)XtRemoveInput(EventId);
  1587. }
  1588.  
  1589. /* Input - Feed data to ghostscript's stdin.
  1590.  * Write bytes to ghostscript using non-blocking I/O.
  1591.  * Also, pipe signals are caught during writing.  The return
  1592.  * values are checked and the appropriate action is taken.  I do
  1593.  * this at this low level, because it may not be appropriate for
  1594.  * SIGPIPE to be caught for the overall application.
  1595.  */
  1596.  
  1597. static void
  1598. Input(gvw)
  1599.     GhostviewWidget gvw;
  1600. {
  1601.     int stat, bbytes;
  1602.     char *ch;
  1603.  
  1604.     /*
  1605.     ** Check for error on previous I/O.
  1606.     */
  1607.     stat = gvw->ghostview.interpreter_input_iosb[0];
  1608.     if(stat != SS$_NORMAL){
  1609.     InterpreterFailed(gvw);
  1610.     } else {
  1611.  
  1612.     /* Get a new section if required */
  1613.     
  1614.     if (gvw->ghostview.ps_input && gvw->ghostview.bytes_left == 0) {
  1615.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1616.         gvw->ghostview.ps_input = ps_old->next;
  1617.         if (ps_old->close) fclose(ps_old->fp);
  1618.         XtFree((char *)ps_old);
  1619.     }
  1620.     if(gvw->ghostview.ps_input){
  1621.         /* Have to seek at the beginning of each section */
  1622.         if (gvw->ghostview.ps_input->seek_needed) {
  1623.         if (gvw->ghostview.ps_input->len > 0)
  1624.             fseek(gvw->ghostview.ps_input->fp,
  1625.               gvw->ghostview.ps_input->begin, SEEK_SET);
  1626.         gvw->ghostview.ps_input->seek_needed = False;
  1627.         gvw->ghostview.bytes_left = gvw->ghostview.ps_input->len;
  1628.         }
  1629.         /*
  1630.         ** Read a line from the file.
  1631.         */
  1632.         ch = fgets(gvw->ghostview.input_buffer, GV_BUFSIZ,
  1633.         gvw->ghostview.ps_input->fp);
  1634.         if(!ch){
  1635.         /*
  1636.         ** Error, EOF when there's supposed to be data left. 
  1637.         */
  1638.         InterpreterFailed(gvw);
  1639.         } else {
  1640.         /*
  1641.         ** Write it to the mailbox.
  1642.         */
  1643.         bbytes = strlen(gvw->ghostview.input_buffer);
  1644.         gvw->ghostview.bytes_left -= bbytes;
  1645.         stat = sys$qio(0, (short)gvw->ghostview.interpreter_input,
  1646.             IO$_WRITEVBLK, &gvw->ghostview.interpreter_input_iosb,
  1647.             IOComplete, 0, gvw->ghostview.input_buffer, bbytes,
  1648.             0, 0, 0, 0);
  1649.         ERR_SIGNAL(stat);
  1650.         }
  1651.     }
  1652.     }
  1653. }
  1654.  
  1655. /* Output - receive I/O from ghostscript's stdout and stderr.
  1656.  * Pass this to the application via the output_callback. */
  1657. static void
  1658. Output(gvw)
  1659.     GhostviewWidget gvw;
  1660. {
  1661.     char buf[GV_BUFSIZ+1];
  1662.     int bytes, stat;
  1663.  
  1664.     stat = gvw->ghostview.interpreter_output_iosb[0];
  1665.     bytes = gvw->ghostview.interpreter_output_iosb[1];
  1666.     if (stat == SS$_NORMAL) {
  1667.     /*
  1668.     ** Got a message. If line complete, pass to the output_callback.
  1669.     **
  1670.     ** HACK ALERT, if bytes is -1 nothing happens, but an I/O is queued.
  1671.     ** This is our first time code, since Xt doesn't queue the I/O for us
  1672.     ** under VMS, just watches for completion. In StartInterpreter We setup
  1673.     ** an IOSB with a success status and -1 bytes so Xt will call us the
  1674.     ** first time to get the I/O queued.
  1675.     */
  1676.     if (bytes == 0) {
  1677.         strcpy(buf, "\n");
  1678.     } else if (bytes == 1) {
  1679.         buf[0] = gvw->ghostview.output_buffer[0];
  1680.         buf[1] = '\0';
  1681.     } else if (bytes > 1) {
  1682.         /*
  1683.         ** Copy the message to a local buffer and pass it to the callback.
  1684.         */
  1685.         memcpy(buf, gvw->ghostview.output_buffer, bytes);
  1686.         buf[bytes] = '\0';
  1687.     }
  1688.     if(bytes >= 0)XtCallCallbackList(gvw, gvw->ghostview.output_callback,
  1689.         (XtPointer) buf);
  1690.     /*
  1691.     ** Queue a new read to the mailbox
  1692.     */
  1693.     stat = sys$qio(0, (short)gvw->ghostview.interpreter_output,
  1694.         IO$_READVBLK, &gvw->ghostview.interpreter_output_iosb, IOComplete,
  1695.         0, gvw->ghostview.output_buffer, GV_BUFSIZ, 0, 0, 0, 0);
  1696.     ERR_SIGNAL(stat);
  1697.     } else {
  1698.     InterpreterFailed(gvw);        /* Something bad happened */
  1699.     }
  1700. }
  1701.  
  1702. /* This routine starts the interpreter.  It sets the DISPLAY and 
  1703.  * GHOSTVIEW environment variables.  The GHOSTVIEW environment variable
  1704.  * contains the Window that ghostscript should write on.
  1705.  *
  1706.  * This routine also opens pipes for stdout and stderr and initializes
  1707.  * application input events for them.  If input to ghostscript is not
  1708.  * from a file, a pipe for stdin is created.  This pipe is setup for
  1709.  * non-blocking I/O so that the user interface never "hangs" because of
  1710.  * a write to ghostscript.
  1711.  */
  1712. static void
  1713. StartInterpreter(w)
  1714.     Widget w;
  1715. {
  1716.     GhostviewWidget gvw = (GhostviewWidget) w;
  1717.     char buf[GV_BUFSIZ];
  1718.     char cmd[512];
  1719.     int ret;
  1720.     short ch1, ch2;
  1721.     char in_mbx_name[65], out_mbx_name[65];
  1722.     long pid, nowait = CLI$M_NOWAIT;
  1723.     const $DESCRIPTOR(ghostview_desc, "GHOSTVIEW");
  1724.     const $DESCRIPTOR(display_desc, "DECW$DISPLAY");
  1725.     const $DESCRIPTOR(lnt_desc, "LNM$PROCESS");
  1726.     $DESCRIPTOR(in_desc, "");
  1727.     $DESCRIPTOR(out_desc, "");
  1728.     $DESCRIPTOR(lnm_desc, "");
  1729.     $DESCRIPTOR(cmd_desc, cmd);
  1730.     ITEM_LIST_3_T(gv_list, 1) = {{{0, LNM$_STRING, buf, NULL}}, 0};
  1731.     ITEM_LIST_3_T(dis_list, 1) = {{{0, LNM$_STRING, NULL, NULL}}, 0};
  1732.     ITEM_LIST_3_T(dvi_list, 1) = {{{64, DVI$_DEVNAM, NULL, NULL}}, 0};
  1733.     IOSB_GET_T dvi_iosb;
  1734.  
  1735.     /*
  1736.     ** Stop interpreter if running
  1737.     */
  1738.     StopInterpreter(w);
  1739.     /*
  1740.     ** Clear the window before starting a new interpreter.
  1741.     */
  1742.     if (gvw->core.background_pixmap != XtUnspecifiedPixmap) {
  1743.     XFillRectangle(XtDisplay(w), gvw->core.background_pixmap,
  1744.                gvw->ghostview.gc,
  1745.                0, 0, gvw->core.width, gvw->core.height);
  1746.     }
  1747.     XClearArea(XtDisplay(w), XtWindow(w),
  1748.            0, 0, gvw->core.width, gvw->core.height, False);
  1749.     /*
  1750.     ** Check for disabled.
  1751.     */
  1752.     if (gvw->ghostview.disable_start) return;
  1753.     /*
  1754.     ** Build Ghostscript startup command
  1755.     */
  1756.     strcpy(cmd, gvw->ghostview.interpreter);
  1757.     strcat(cmd, " ");
  1758.     if (gvw->ghostview.quiet) strcat(cmd, "\"-dQUIET\" ");
  1759.     strcat(cmd, "\"-dNOPAUSE\" ");
  1760.     if (gvw->ghostview.preload) {
  1761.     strcat(cmd, gvw->ghostview.preload);
  1762.     strcat(cmd, " ");
  1763.     }
  1764.     strcat(cmd, "\"-\" ");
  1765.  
  1766.     /*
  1767.     ** Determine input source.
  1768.     */
  1769.     if (gvw->ghostview.filename == NULL) {
  1770.     /*
  1771.     ** Create a mailbox to feed input to Ghostscript and get its name.
  1772.     */
  1773.     ret = sys$crembx(0, &ch1, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
  1774.     ERR_SIGNAL(ret);
  1775.     dvi_list.item[0].buffer_p = in_mbx_name;
  1776.     ret = sys$getdvi(0, ch1, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
  1777.     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
  1778.     in_mbx_name[64] = '\0';
  1779.     in_desc.dsc$a_pointer = in_mbx_name;
  1780.     in_desc.dsc$w_length = strlen(in_mbx_name);
  1781.     } else {
  1782.     /*
  1783.     ** Set up file name to give Ghostscript as standard input.
  1784.     */
  1785.     in_desc.dsc$a_pointer = gvw->ghostview.filename;
  1786.     in_desc.dsc$w_length = strlen(gvw->ghostview.filename);
  1787.     }
  1788.     /*
  1789.     ** Create mailbox to receive Ghostscript's output
  1790.     */
  1791.     ret = sys$crembx(0, &ch2, GV_BUFSIZ, GV_BUFSIZ, 0, 0, 0, 0);
  1792.     ERR_SIGNAL(ret);
  1793.     dvi_list.item[0].buffer_p = out_mbx_name;
  1794.     ret = sys$getdvi(0, ch2, 0, &dvi_list, &dvi_iosb, 0, 0, 0);
  1795.     ERR_SIGNAL(ret); ERR_SIGNAL(dvi_iosb.status);
  1796.     out_mbx_name[64] = '\0';
  1797.     out_desc.dsc$a_pointer = out_mbx_name;
  1798.     out_desc.dsc$w_length = strlen(out_mbx_name);
  1799.     /*
  1800.     ** Create GHOSTVIEW and DECW$DISPLAY logical names.
  1801.     **
  1802.     ** We use CRELNM rather than LIB$SET_LOGICAL because we want these to be
  1803.     ** user mode and go away when the program exits. It doesn't matter that we
  1804.     ** may set them multiple times, as with the mailbox logicals, since once
  1805.     ** Ghostscript starts we don't need them any more.
  1806.     */
  1807.     sprintf(buf, "%d", XtWindow(w));
  1808.     gv_list.item[0].buffer_size = strlen(buf);
  1809.     ret = sys$crelnm(0, &lnt_desc, &ghostview_desc, 0, &gv_list);
  1810.     ERR_SIGNAL(ret);
  1811.     dis_list.item[0].buffer_p = XDisplayString(XtDisplay(w));
  1812.     dis_list.item[0].buffer_size = strlen(dis_list.item[0].buffer_p);
  1813.     ret = sys$crelnm(0, &lnt_desc, &display_desc, 0, &dis_list);
  1814.     ERR_SIGNAL(ret);
  1815.     /*
  1816.     ** Spawn Ghostscript process
  1817.     */
  1818.     gvw->ghostview.changed = False;
  1819.     gvw->ghostview.busy = True;
  1820.     cmd_desc.dsc$w_length = strlen(cmd);
  1821.     ret = lib$spawn(&cmd_desc, &in_desc, &out_desc, &nowait, 0, &pid, 0, 0,
  1822.     0, 0, 0, 0, 0);
  1823.     ERR_SIGNAL(ret);
  1824.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.busy_cursor);
  1825.     /*
  1826.     ** Everything worked, initialize IOSBs and save info about interpretter.
  1827.     */
  1828.     gvw->ghostview.interpreter_pid = pid;
  1829.     if (gvw->ghostview.filename == NULL) {
  1830.     gvw->ghostview.interpreter_input = ch1;
  1831.     gvw->ghostview.interpreter_input_iosb[0] = 0;
  1832.     }
  1833.     gvw->ghostview.interpreter_output = ch2;
  1834.     if (gvw->ghostview.output_buffer == NULL) {
  1835.     gvw->ghostview.output_buffer = XtMalloc(GV_BUFSIZ);
  1836.     }
  1837.     GLInsert(gvw);
  1838.     /*
  1839.     ** Fake a completed I/O so Output will get called to queue the first I/O.
  1840.     */
  1841.     gvw->ghostview.interpreter_output_iosb[0] = SS$_NORMAL;
  1842.     gvw->ghostview.interpreter_output_iosb[1] = -1;
  1843.     IOComplete();
  1844. }
  1845.  
  1846. /* Stop the interperter, if present, and remove any Input sources. */
  1847. /* Also reset the busy state. */
  1848. static void
  1849. StopInterpreter(w)
  1850.     Widget w;
  1851. {
  1852.     int ret;
  1853.     
  1854.     GhostviewWidget gvw = (GhostviewWidget) w;
  1855.     if (gvw->ghostview.interpreter_pid >= 0) {
  1856.     ret = sys$delprc(&gvw->ghostview.interpreter_pid, 0);
  1857.     if(ret != SS$_NORMAL && ret != SS$_NONEXPR)lib$signal(ret, 0, 0);
  1858.     gvw->ghostview.interpreter_pid = -1;
  1859.     }
  1860.     if (gvw->ghostview.interpreter_input >= 0) {
  1861.     (void) sys$dassgn(gvw->ghostview.interpreter_input);
  1862.     gvw->ghostview.interpreter_input = -1;
  1863.     while (gvw->ghostview.ps_input) {
  1864.         struct record_list *ps_old = gvw->ghostview.ps_input;
  1865.         gvw->ghostview.ps_input = ps_old->next;
  1866.         if (ps_old->close) fclose(ps_old->fp);
  1867.         XtFree((char *)ps_old);
  1868.     }
  1869.     }
  1870.     if (gvw->ghostview.interpreter_output >= 0) {
  1871.     (void) sys$dassgn(gvw->ghostview.interpreter_output);
  1872.     gvw->ghostview.interpreter_output = -1;
  1873.     }
  1874.     gvw->ghostview.busy = False;
  1875.     XDefineCursor(XtDisplay(gvw), XtWindow(gvw), gvw->ghostview.cursor);
  1876.     GLRemove(gvw);
  1877. }
  1878.  
  1879. /* GhostviewSendPS:
  1880.  *   Queue a portion of a PostScript file for output to ghostscript.
  1881.  *   fp: FILE * of the file in question.  NOTE: if you have several
  1882.  *   Ghostview widgets reading from the same file.  You must open
  1883.  *   a unique FILE * for each widget.
  1884.  *   SendPS does not actually send the PostScript, it merely queues it
  1885.  *   for output.
  1886.  *   begin: position in file (returned from ftell()) to start.
  1887.  *   len:   number of bytes to write.
  1888.  *
  1889.  *   If an interpreter is not running, nothing is queued and
  1890.  *   False is returned.
  1891.  */
  1892. Boolean
  1893. GhostviewSendPS(w, fp, begin, len, close)
  1894.     Widget w;
  1895.     FILE *fp;
  1896.     long begin;
  1897.     unsigned int len;
  1898.     Bool close;
  1899. {
  1900.     GhostviewWidget gvw = (GhostviewWidget) w;
  1901.     struct record_list *ps_new;
  1902.  
  1903.     if (gvw->ghostview.interpreter_input < 0) return False;
  1904.     if(len != 0){
  1905.     ps_new = (struct record_list *) XtMalloc(sizeof (struct record_list));
  1906.     ps_new->fp = fp;
  1907.     ps_new->begin = begin;
  1908.     ps_new->len = len;
  1909.     ps_new->seek_needed = True;
  1910.     ps_new->close = close;
  1911.     ps_new->next = NULL;
  1912.  
  1913.     if (gvw->ghostview.input_buffer == NULL) {
  1914.         gvw->ghostview.input_buffer = XtMalloc(GV_BUFSIZ);
  1915.     }
  1916.  
  1917.     if (gvw->ghostview.ps_input == NULL) {
  1918.         gvw->ghostview.bytes_left = len;
  1919.         gvw->ghostview.ps_input = ps_new;
  1920.         /*
  1921.         ** Fake a completed I/O so Input will get called to queue the
  1922.         ** first I/O.
  1923.         */
  1924.         gvw->ghostview.interpreter_input_iosb[0] = SS$_NORMAL;
  1925.         gvw->ghostview.interpreter_input_iosb[1] = -1;
  1926.         IOComplete();
  1927.     } else {
  1928.         struct record_list *p = gvw->ghostview.ps_input;
  1929.         while (p->next != NULL) {
  1930.         p = p->next;
  1931.         }
  1932.         p->next = ps_new;
  1933.     }
  1934.     }
  1935.     return True;
  1936. }
  1937. #endif /* VMS */
  1938.